package com.apes.fico.engine;

import com.apes.framework.jpa.ApesBean;
import com.apes.framework.plugin.express.IEngine;
import com.apes.framework.util.CacheUtil;
import com.apes.framework.util.MD5Util;
import com.apes.framework.util.PropertiesUtils;
import com.apes.scm.masterdata.party.model.PartyRole;
import org.apache.log4j.Logger;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

import static com.apes.framework.util.ReflectUtil.getFieldValue;
import static com.apes.framework.util.ReflectUtil.getMerge;

/**
 * 功能：
 *
 * @author xul
 * @create 2019-01-30 16:23
 */
public class FicoUtil {
    protected static final Logger logger = Logger.getLogger(FicoUtil.class);
    private static final String NODE_NAME = "ae_node_name";
    private static final String NODE_PATH = "ae_node_path";
    private static final String PARENT_NODE = "ae_parent_node";
    private static final String FIELD_NAME_COMPANY = "company";

    /**
     * 创建当前节点上下文
     **/
    static Map<String, Object> createContext(ApesBean entity, Map<String, Object> parent) {
        Map<String, Object> context = getMerge(entity);
        String nodeName = getNodeName(entity);
        context.put(NODE_NAME, nodeName);
        if (parent != null) {
            StringBuilder sb = new StringBuilder();
            Object parentPath = parent.get(NODE_PATH);
            if (parentPath != null) sb.append(parentPath).append(".");
            context.put(NODE_PATH, sb.append(nodeName).toString());
            context.put(PARENT_NODE, parent);
            parent.put(nodeName, context);
            getRootContext(parent).put(nodeName, context);
        }
        return context;
    }

    /**
     * 移除当前节点上下文
     **/
    static void removeContext(final Map<String, Object> context) {
        String nodeName = getNodeName(context);
        Map<String, Object> parent = getParentContext(context);
        if (parent != null) parent.remove(nodeName);
        Map<String, Object> root = getRootContext(context);
        if (root != null) root.remove(nodeName);
    }

    /**
     * 取当前节点上下文的父节点
     **/
    static Map<String, Object> getParentContext(Map<String, Object> context) {
        Object parent = context.get(PARENT_NODE);
        return parent == null ? null : (Map) parent;
    }

    /**
     * 检查当前节点是否根节点
     **/
    public static boolean isRootContext(Map<String, Object> context) {
        return getParentContext(context) == null;
    }

    /**
     * 检查元素是否是节点
     **/
    private static boolean isContext(Object context) {
        return context instanceof Map;
    }

    /**
     * 取当前节点上下文的根节点
     **/
    private static Map<String, Object> getRootContext(Map<String, Object> context) {
        Object parent = context.get(PARENT_NODE);
        if (parent == null) return context;
        return getRootContext((Map<String, Object>) parent);
    }

    /**
     * 取节点路径
     **/
    static String getPath(Map context) {
        Object path = context.get(NODE_PATH);
        return path != null ? (String) path : null;
    }

    /**
     * 取节点名
     **/
    public static String getNodeName(Map context) {
        Object nodeName = context.get(NODE_NAME);
        return nodeName != null ? (String) nodeName : null;
    }

    /**
     * 取实体名
     **/
    static String getNodeName(ApesBean entity) {
        return entity.getClass().getSimpleName();
    }

    /**
     * 取公司
     **/
    public static PartyRole getCompany(ApesBean entity) {
        return getFieldValue(entity, FIELD_NAME_COMPANY);
    }
    /**
     * 取公司
     **/
    public static PartyRole getCompany(ApesBean entity,String fieldName) {
        return getFieldValue(entity, fieldName);
    }
    /**
     * 执行表达式，有错误则抛异常
     **/
    static <T> T eval(final String express, final Map<String, Object> context, boolean requireCheck, Function<Exception, String> func) {
        String target;
        try {
            target = replace(context, express, requireCheck);
        } catch (Exception e) {
            throw new RuntimeException(func.apply(e));
        }
        IEngine<String, Map<String, Object>> engine = CacheUtil.getEngine();
        T result = engine.execute(target, getRootContext(context), func);
        if (result == null) throw new RuntimeException(func.apply(new RuntimeException("值为空")));
        return result;
    }

    /**
     * 执行表达式，有错误不抛异常，给默认值
     **/
    public static <T> T eval(final String express, final Map<String, Object> context, T defaults, boolean requireCheck, Function<Exception, String> func) {
        String target;
        try {
            target = replace(context, express, requireCheck);
        } catch (Exception e) {
            logger.warn(e.getMessage());
            return defaults;
        }
        T result = (T) CacheUtil.getEngine().execute(target, getRootContext(context), defaults, func);
        if (result == null) result = defaults;
        return result;
    }

    /**
     * 替换表达式中的占位符
     **/
    public static String replace(final Map<String, Object> context, final String express, boolean requireCheck) {
        List<String> fields = new ArrayList<>();
        String target = PropertiesUtils.replace(express, field -> {
            if (requireCheck && !check(context, field)) {
                throw new RuntimeException("表达式：" + express + " 中的变量：" + field + " 无效！");
            }
            fields.add(field);
            return field;
        });

        String nodeName = getNodeName(context);
        if (requireCheck && !isRootContext(context) && fields.stream().filter(f -> split(f)[0].equalsIgnoreCase(nodeName)).count() == 0) {
            throw new RuntimeException("表达式：" + express + " 无效！");
        }
        return target;
    }

    /**
     * 检查变量是否存在于当前上下文中
     **/
    public static boolean check(Map<String, Object> context, final String field) {
        String[] arr = split(field);
        if (isRootContext(context)) { /*处理没有节点前缀的属性*/
            if (context.containsKey(field)) return true;
            Object o = context.get(arr[0]);
            if (context.containsKey(arr[0]) && !isContext(o)) return true;
        } else {/*处理有节点前缀的属性*/
            if (arr.length > 1 && Objects.requireNonNull(getNodeName(context)).equalsIgnoreCase(arr[0]) && context.containsKey(arr[1])) {
                return true;
            }
        }
        Map<String, Object> parent = getParentContext(context);
        return parent != null && check(parent, field);
    }

    public static String[] split(String field) {
        return field.split("\\.");
    }

    /**
     * 创建归集key
     **/
    public static String createCollectKey(List<?> rows, Function<Object, String> func) {
        if (CollectionUtils.isEmpty(rows)) return null;
        StringBuffer sb = new StringBuffer();
        rows.forEach(row -> {
            if (sb.length() > 0) sb.append(",");
            sb.append(func.apply(row));
        });
        return MD5Util.encrypt(sb.toString());
    }
}
